using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

using System.Reflection;
using System.IO;

namespace Athena
{
    public class PluginManager
    {
        private static PluginManager _oInstance = new PluginManager();

        public static PluginManager Instance
        {
            get { return _oInstance; }
        }

        private PluginManager()
        {
            _oPluginAssemblies.Add(typeof(PluginManager).Assembly);
        }

        public void AddPluginsFromFile(string sPath)
        {
            try
            {
                _oPluginAssemblies.Add(Assembly.LoadFile(sPath));
            }
            catch (Exception)
            {
            }
            InstantiateTypes();
            OnPluginsChanged();
        }

        public void AddPluginsFromDirectory(string sPath)
        {
            if (!Directory.Exists(sPath)) return;

            string[] sFiles = Directory.GetFiles(sPath, "*.dll");
            foreach (string s in sFiles)
            {
                try
                {
                    _oPluginAssemblies.Add(Assembly.LoadFile(s));
                }
                catch (Exception e) 
                {
                    System.Diagnostics.Debug.WriteLine(e.Message);
                }
            }
            InstantiateTypes();
            OnPluginsChanged();
        }

        public delegate void PluginsChangedDelegate(object sender, EventArgs e);
        public event PluginsChangedDelegate PluginsChanged;
        // Invoke the Changed event; called whenever list changes:
        public virtual void OnPluginsChanged()
        {
            if (PluginsChanged != null)
            {
                PluginsChanged(this, EventArgs.Empty);
            }
        }


        List<Assembly> _oPluginAssemblies = new List<Assembly>();


        /// <summary>
        ///  returns all current plugin types of the given type
        /// </summary>
        /// <param name="oType"></param>
        /// <returns></returns>
        public List<Type> GetPlugins(Type oType)
        {
            List<Type> oReturnTypes = new List<Type>();
            foreach (Assembly assembly in _oPluginAssemblies)
            {
                Type[] oTypes = assembly.GetExportedTypes();
                foreach (Type type in oTypes)
                {
                    if (Utility.IsType(oType, type))
                        oReturnTypes.Add(type);
                }
            }
            return oReturnTypes;
        }

        public void InstantiateTypes()
        {
            InstantiateTypes(typeof(Athena.Objects.IGlyph));
            InstantiateTypes(typeof(Athena.Tools.INonVisualTool));
            InstantiateTypes(typeof(Athena.Tools.IVisualTool));
            InstantiateTypes(typeof(Athena.Tools.IGUITool));
        }

        List<Type> _AlreadyInstantiatedTypes = new List<Type>();
        public void InstantiateTypes(Type targetType)
        {
            if (_AlreadyInstantiatedTypes == null) _AlreadyInstantiatedTypes = new List<Type>();

            List<Type> pluginTypes = GetPlugins(targetType);
            foreach (Type type in pluginTypes)
            {
                try
                {
                    if (!type.IsAbstract && !_AlreadyInstantiatedTypes.Contains(type))
                    {                   
                        Activator.CreateInstance(type, true);
                        _AlreadyInstantiatedTypes.Add(type);
                    }
                }
                catch (Exception)
                {
                }
            }
        }

        public List<string> AllGlyphs
        {
            get
            {
                List<string> oResult = new List<string>();

                foreach (Type t in _AlreadyInstantiatedTypes)
                {
                    if (Utility.IsType(typeof(Athena.Objects.IGlyph), t))
                        oResult.Add(t.Name);
                }

                return oResult;

            }
        }

        private Hashtable _EnabledGlyphs;
        public Hashtable EnabledGlyphs
        {
            get { return _EnabledGlyphs; }
            set { _EnabledGlyphs = value; }
        }
    }
}
